#pragma once

#include <iostream>
#include <unordered_map>
#include <functional>
#include <fstream>
#include <vector>
#include <string>
#include "Logger.hpp"

using namespace std;

static const string space = " ";
static const string colon_space = ": ";
static const string newline = "/r/n";
static const string webroot = "./wwwroot";
static const string testfile = "index.html";
static const string qmark = "?";
static const string suffixstring = ".";

class HttpRequest
{
private:
    string ReadOneLine(string &post, bool *status)
    {
        auto pos = post.find(newline);
        if (pos == string::npos)
        {
            *status = false;
            return string();
        }
        *status = true;
        string new_post = post.substr(0, pos);
        post.erase(0, pos + newline.size());
        return new_post;
    }

    void ReadRequestLine(string &post)
    {
        stringstream ss(post);
        ss >> _method >> _uri >> _http_version;
        LOG(LogLevel::DEBUG) << "_method:" << _method;
        LOG(LogLevel::DEBUG) << "_uri:" << _uri;
        LOG(LogLevel::DEBUG) << "_http_version:" << _http_version;
    }

    void BuildKV(string &post, string *K, string *V)
    {
        auto pos = post.find(colon_space);
        if (pos == string::npos)
        {
            *K = *V = string();
            return;
        }
        *K = post.substr(0, pos);
        *V = post.substr(pos + colon_space.size());
    }

public:
    HttpRequest() {}
    void Serialize() {} // 已经保证是完整报文不需要序列化

    bool DeSerialize(string &reqstr) //  服务器进行反序列化取报文
    {
        bool status = true;
        string line = ReadOneLine(reqstr, &status);
        if (status == false)
            return false;
        ReadRequestLine(line);

        while (true)
        {
            status = true;
            line = ReadOneLine(reqstr, &status);
            if (status && !reqstr.empty())
            {
                string k, v;
                BuildKV(line, &k, &v);
                if (k.empty() || v.empty())
                    continue;
                _req_header.insert(make_pair(k, v));
            }
            else if (status)
            {
                _blank_line = line;
                break;
            }
            else
            {
                LOG(LogLevel::DEBUG) << "非法请求";
                return false;
            }
        }

        _path = webroot;
        _path += _uri;
        if (_uri == "/")
        {
            _path += testfile;
        }
        if (_method == "GET")
        {
            auto pos = reqstr.find(qmark);
            _path = _path.substr(0, pos);
            _req_body = _path.substr(pos + qmark.size());
        }
        else if (_method == "POST")
        {
            _req_body = reqstr;
        }
        else
        {
            LOG(LogLevel::DEBUG) << "暂时没有该方法";
            return false;
        }
        return true;
    }

    string Path()
    {
        return _path;
    }

    void SetPath(string path)
    {
        _path = path;
    }

    string Body()
    {
        return _req_body;
    }

    string Suffix()
    {
        if (_path.empty())
            return string();
        else
        {
            auto pos = _path.find(suffixstring);
            if (pos == string::npos)
                return string();
            else
                return _path.substr(pos);
        }
    }

    ~HttpRequest() {}

private:
    string _method;
    string _uri;
    string _req_body;
    string _http_version;
    unordered_map<string, string> _req_header;
    vector<string> _cookie;
    string _path;
    string _blank_line;
};

class HttpResponse
{
private:
    string Code_to_desc(int code)
    {
        switch (code)
        {
        case 200:
            return "OK";
        case 301:
            return "Moved Permantly";
        case 302:
            return "See Other";
        case 307:
            return "Temporary Redireact";
        case 400:
            return "Bad Request";
        case 404:
            return "Not Find";
        default:
            return "";
        }
    }

public:
    HttpResponse()
        : _http_version("HTTP/1.1"),
          _blank_line(newline)
    {
    }
    bool Deserialize() {}

    string Serialize()
    {
        string resp_str = _http_version + space + to_string(_code) +
                          space + _code_desc + newline;
        if (!_resp_body.empty())
        {
            string len = to_string(_resp_body.size());
            SetHeader("Content-Length", len);
        }
        for (const auto &e : _resp_header)
        {
            resp_str += e.first + colon_space + e.second + newline;
        }
        for (const auto &cookie : _cookie)
        {
            resp_str += cookie + newline;
        }
        resp_str += _blank_line;
        resp_str += _resp_body;

        return resp_str;
    }

    bool ReadContent(const std::string &path)
    {
        // 以二级制方式读取
        // 打开文件，二进制模式
        std::ifstream file(path, std::ios::binary);
        if (!file.is_open())
        {
            LOG(LogLevel::WARNING) << path << " 资源不存在!";
            return false;
        }

        // 定位到文件末尾获取文件大小
        file.seekg(0, std::ios::end);
        std::streampos fileSize = file.tellg(); // 获取文件大小
        file.seekg(0, std::ios::beg);

        // 创建足够大的字符串来保存数据
        _resp_body.resize(static_cast<size_t>(fileSize));

        // 读取文件内容到字符串
        file.read(&_resp_body[0], fileSize);
        file.close();

        return true;
    }

    void SetCode(const int &code)
    {
        if (code >= 100 && code < 600)
        {
            _code = code;
            _code_desc = Code_to_desc(_code);
        }
        else
        {
            LOG(LogLevel::FATAL) << "非法请求 " << code;
        }
    }

    bool SetHeader(const string &k, const string &v)
    {
        if (k.empty() || v.empty())
            return false;
        _resp_header[k] = v;
        return true;
    }

    void SetCookie(string &k, string &v)
    {
        if (k.empty() || v.empty())
        {
            k = v = string();
            return;
        }
        string cookie = k;
        k += colon_space;
        k += v;
        _cookie.push_back(cookie);
    }

    ~HttpResponse() {}

private:
    string _http_version;
    int _code;
    string _code_desc;
    unordered_map<string, string> _resp_header;
    vector<string> _cookie;
    string _resp_body;
    string _blank_line;
};

using func_t = function<HttpResponse(HttpRequest &)>;

class Http
{
public:
    string Suffix_to_Desc(const string &suffix)
    {
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".css")
            return "text/css";
        else if (suffix == ".js")
            return "application/x-javascript";
        else if (suffix == ".png")
            return "image/png";
        else if (suffix == ".jpg")
            return "image/jpeg";
        else if (suffix == ".txt")
            return "text/plain";
        else
            return "text/html";
    }

public:
    Http() {}

    void Register(const string &action, func_t handler)
    {
        string key = webroot;
        key += action;
        _http_solve[key] = handler;
    }

    string RespStr(string &req)
    {
        string respstr;
        HttpRequest req_http;
        cout << req << endl;
        if (req_http.DeSerialize(req))
        {
            HttpResponse resp_http;
            string target = req_http.Path();
            if (_http_solve.find(target) != _http_solve.end())
            {
                resp_http = _http_solve[target](req_http);
            }
            else
            {
                if (resp_http.ReadContent(target))
                {
                    string suffix = req_http.Suffix();
                    string mine_typr = Suffix_to_Desc(suffix);
                    resp_http.SetHeader("Content-Type", mine_typr);
                    resp_http.SetCode(200);
                }
                else
                {
                    resp_http.SetCode(301);
                    resp_http.SetHeader("Location", "/404.html");
                }
            }
            respstr = resp_http.Serialize();
        }
        return respstr;
    }

    ~Http() {}

private:
    unordered_map<string, func_t> _http_solve;
};